package dm

import (
	"fmt"
	"runtime"
	"sync"
	"time"
)

/**
轻量级的线程
非抢占式多任务处理，由协程主动交出控制权 什么时候交出控制权是由协程内部决定的
线程是操作系统的切换 线程是抢占式的 可以被操作系统掐掉
它是编译器/解释器/虚拟机层面的多任务
线程是操作系统层面的
协程是后面调度器调度的。
多个协程可以在一个或者多个线程上执行，这个是有内部调度器决定的。

// 什么情况会自动切换协程
1、遇到 主动释放 runtime.Gosched()
2、遇到io 比如fmt.Printf("") 这种输出的 屏幕打印字 网络 磁盘等等
3、select
4、channel
5、等待锁
6、函数调用（有时）
以上就参考不保证每次都切换


通过命令行检测资源是否有抢占冲突
go run -race goroutine.go

go 协程模型
线程1【协程1，协程2】，线程2【协程3，协程4】，线程3【协程5】
===============调度器（协程谁和谁在一起在一个线程这个是调度决定的）==============
go 关键字就是协程东西给到调度器 调度器在合适的点进行切换什么点上面讲了 // 什么情况会自动切换协程

查看系统默认跑了多少线程
GoRutine 看top 的 #th 7/活动线程数 活动的根据系统的核数来开

不要通过共享内存来通信， 通过通信来共享内存。

chan 后面可以跟任意类型 包括自定义类型

*/

func GoRutine() {
	fmt.Println("goroutine")

	// 在其他语言如果开的线程太多可能需要aio但是程序不用管开就得了
	for i := 0; i < 100; i++ {
		go func(i int) {
			for {
				fmt.Println("协程", i)
			}
		}(i)
	}

	// 特点1 如果主线程退出了执行结束了，所有协程全部退出。
	// 解决方案1延时主线程退出
	// 协程是开在线程里面的
	time.Sleep(time.Second * 20)
}

func KillGoRoutine() {
	// 被一个协程给强掉不切换上下文了
	a := make([]int, 100)
	for i := 0; i < 100; i++ {
		go func(i int) {
			for {
				a[i]++ // 一直在用没有释放 从打印结果来看就8个协程在跑因为默认是8个线程散列到不通线程因为独占所以其他协程没机会切换上下文
				// 手动交出控制权 可以看到随机交出控制权其他散列到同一线程的协程有机会临幸了
				// 系统默认开和cpu核数相匹配的线程 协程全部都散列进去。可能一个线程有n个协程这就要切换上下文
				if i%2 == 0 {
					runtime.Gosched()
				}
			}
		}(i)
	}
	time.Sleep(time.Millisecond)

	fmt.Println(a)
	// go run -race 这样还是有抢占 a 在下面打印 上面写入 用 channel解决
}

func ChannelRead(c <-chan int) {

}
func ChannelWrite(c chan<- int) {

}
func RC() chan int {
	ch := make(chan int, 10)
	go func() {
		ch <- 1
	}()
	return ch
}

func Channel() {
	fmt.Println("channel")
	var c chan int // 默认值nil 这种channel是有用的 select <-c 这种空的是不会走的 有值才走case <-c:
	fmt.Println(c)

	// 申请个channel 满了和空了在读取写都溢出 死锁 deadlock
	// 如果不想开空间  必须有个读的有个写的同时搞。
	cx := make(chan int, 10)
	cx <- 1
	fmt.Println(<-cx)

	//// 同时写
	//go func() {
	//	for {
	//		cx <- 1
	//	}
	//}()
	//
	//// 同时读
	//go func() {
	//	for {
	//		fmt.Println(<-cx)
	//		time.Sleep(time.Millisecond)
	//	}
	//}()
	//
	//time.Sleep(time.Minute)

	// close(channel) 这个配合range 来使用 如果用for{如果外边close这里收到的永远是 chan int int默认的值} 如果是其它类型就是其它类型的默认值
	// for { 可以用  d,ok := <-ch} ok 来判断是否还有值 if !ok {break} 用range就不需要这些乱七八糟的 但是range 必须有close否则死循环

	// close是写入的close的
}

var lk sync.WaitGroup // 注意这是声明

func Syncs() {
	lk.Add(1) // 起了多少协程
	ch := make(chan int, 1)
	go func() {
		sum := 0
		for i := 0; i < 10000000; i++ {
			// fmt.Println(i)
			sum += i
		}
		ch <- sum
		lk.Done() // 监控协程总数-1 0 的时候wait就结束了
	}()

	fmt.Println(<-ch, "123")

	lk.Wait() // 等待所有协程都完成
}

// 通知外边做完了
type worker struct {
	in   chan int
	done chan bool
}

func Tz() {
	w := worker{
		make(chan int, 100),
		make(chan bool, 1),
	}
	go func(in chan int, done chan bool) {
		sum := 0
		for i := 0; i < 1000; i++ {
			// fmt.Println(i)
			sum += i
			in <- sum
			done <- false
		}

		done <- true
	}(w.in, w.done)

	// 外边消费
	go func() {
		for {
			fmt.Println(<-w.in)
			if <-w.done {
				break
			}
		}
	}()

}

type wker struct {
	in    chan int
	dones chan int
}

func CreateWorker() wker {
	w := wker{
		make(chan int, 1),
		make(chan int, 1), // 完成了向外发信号
	}

	go func(in chan int, done chan int) {
		sum := 0
		for i := 0; i < 10; i++ {
			sum += i
			// 如果卡住了尝试在这 尝试 go func(){<-sum}()
			in <- sum
			done <- 0
		}
		done <- 1 // 向外发信号
	}(w.in, w.dones)

	return w
}

func More() {
	var wkers = make([]wker, 10)

	for i := 0; i < 10; i++ {
		wkers[i] = CreateWorker()
	}
	for _, v := range wkers {
		for {
			if <-v.dones == 0 {
				fmt.Println(<-v.in)
			} else {
				break
			}
		}
	}

}
